use std::{sync::Arc, io::{Write, IoSlice, Seek, Read, IoSliceMut}};
use std::ops::Deref;


use super::{FixedContainer, XcontainerResult, XcontainerError};

pub type OffsetMeta = u32;

pub struct DynContainer<M:Copy,T,const C:usize> {
    meta:Arc<FixedContainer<(OffsetMeta,M),C>>,
    items:Vec<Arc<T>>,
}

impl<M:Copy,T,const C:usize> DynContainer<M,T,C> {
    pub fn new() -> Self {
        Self {
            meta:Arc::new(FixedContainer::new()),
            items:Vec::new(),
        }
    }

    pub fn meta(&self) -> &FixedContainer<(OffsetMeta,M),C> {
        &self.meta
    }
}


impl<M:Copy,T,const C:usize> Deref for DynContainer<M,T,C> {
    type Target = Vec<Arc<T>>;
    fn deref(&self) -> &Self::Target {
        &self.items
    }
}

impl<T:DynContainerWriteItem,const C:usize> DynContainer<T::Meta,T,C> {
    pub fn half(meta:Arc<FixedContainer<(OffsetMeta,T::Meta),C>>) -> Self {
        Self {
            meta:meta,
            items:Vec::new(),
        }
    }

    pub fn append_item(&mut self,item:Arc<T>) -> XcontainerResult<usize> {
        let data_len = item.as_data().len();
        let no = Arc::get_mut(&mut self.meta)
            .expect("meta borrow before init")
            .append_item((data_len.try_into().unwrap(),item.to_metadata()))?;
        self.items.push(item);
        Ok(no)
    }

    pub fn on_vec_write<W:Write+Seek>(&self,writer:&mut W) -> XcontainerResult<(u64,Arc<FixedContainer<(OffsetMeta,T::Meta),C>>)> {
        let write_position = writer
            .stream_position()
            .map_err(|e|XcontainerError::DynContainerSeek(e))?;
        let io_vec:Vec<_> = self
            .items
            .iter()
            .map(|t|IoSlice::new(t.as_data()))
            .into_iter()
            .collect();
        writer
            .write_vectored(&io_vec)
            .map_err(|e|XcontainerError::DynContainerWrite(e))?;
        Ok((write_position,self.meta.clone()))
    }
}


impl<T:DynContainerReadItem,const C:usize> DynContainer<T::Meta,T,C> {
    pub fn on_vec_read<R:Read+Seek>(reader:&mut R,meta:Arc<FixedContainer<(OffsetMeta,T::Meta),C>>) -> XcontainerResult<Self> {
        let mut items:Vec<_> = meta
            .iter()
            .map(|t|Arc::new(T::from_meta(&t)))
            .into_iter()
            .collect();
        let mut io_vec:Vec<_> = items
            .iter_mut()
            .map(|t|
                IoSliceMut::new(Arc::get_mut(t).expect("unreachable").as_data_mut()
            ))
            .into_iter()
            .collect();
        reader
            .read_vectored(&mut io_vec)
            .map_err(|e|XcontainerError::DynContainerWrite(e))?;
        Ok(Self {
            meta:meta,
            items:items,
        })
    }
}

pub trait DynContainerWriteItem {
    type Meta:Copy;
    fn to_metadata(&self) -> Self::Meta;
    fn as_data(&self) -> &[u8];
}
pub trait DynContainerReadItem {
    type Meta:Copy;
    fn from_meta(meta:&(OffsetMeta,Self::Meta)) -> Self;
    fn as_data_mut(&mut self) -> &mut [u8];
}